package com.learn.security.theory.asymmetric_encryption;

import com.learn.security.utils.asymmetric_encryption.RSAUtil;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;

/**
 * RSA加密
 */
public class RSATest {

    public static final String encodeStr = "rsa test";

    public static void main(String[] args) {
        rasUtil();
        jdkRSA();
    }

    // 用RASUtil实现：RSA
    private static void rasUtil() {
        String publicKey;
        String privateKey;


        // 1.初始化发送方密钥
        Map<String, Object> keyMap = RSAUtil.initKey();
        publicKey = RSAUtil.getPublicKey(keyMap);
        privateKey = RSAUtil.getPrivateKey(keyMap);
        System.err.println("公钥: " + publicKey);
        System.err.println("私钥：" + privateKey);

        // 1.1 根据私钥，公钥 获取 n，e
        RSAPrivateKey rsaPrivateKey = RSAUtil.getPrivateKeyOriginal(keyMap);
        RSAPublicKey rsaPublicKey = RSAUtil.getPublicKeyOriginal(keyMap);

        BigInteger privateKey_n = RSAUtil.getModulus(rsaPrivateKey);
        BigInteger privateKy_d = RSAUtil.getPrivateExponent(rsaPrivateKey);

        BigInteger publicKey_n = RSAUtil.getModulus(rsaPublicKey);
        BigInteger publicKey_e = RSAUtil.getPublicExponent(rsaPublicKey);

        System.err.println("privateKey_n:" + privateKey_n + "\npublicKey_n:" + publicKey_n + "\n是否相等：" + privateKey_n.equals(publicKey_n));
        System.err.println("privateKy_d:" + privateKy_d + "\npublicKey_e:" + publicKey_e + "\n是否相等：" + privateKy_d.equals(publicKey_e));

        // 1.1 验证根据n，e获取公钥
        String publicKey1 = RSAUtil.getPublicKey(publicKey_n.toByteArray(), publicKey_e.toByteArray());
        System.err.println("两个公钥 是否相等：" + publicKey.equals(publicKey1));


        // 2.公钥加密、私钥解密
        System.out.println("公钥加密——公钥解密");

        // 2.1 公钥加密
        byte[] encodedData = RSAUtil.encryptByPublicKey(encodeStr.getBytes(), publicKey);
        System.err.println("公钥加密后: " + RSAUtil.encryptBASE64(encodedData));
        System.out.println("公钥加密后:" + Base64.encodeBase64String(encodedData));

        // 2.2 私钥加密
        byte[] decodedData = RSAUtil.decryptByPrivateKey(encodedData, privateKey);
        String result = decodedData == null ? null : new String(decodedData);
        System.err.println("私钥解密后: " + result);

        // 3.私钥加密、公钥解密
        System.out.println("私钥加密——公钥解密");

        // 3.1 私钥加密
        encodedData = RSAUtil.encryptByPrivateKey(encodeStr.getBytes(), privateKey);
        System.err.println("私钥加密后: " + RSAUtil.encryptBASE64(encodedData));

        // 3.2 公钥解密
        decodedData = RSAUtil.decryptByPublicKey(encodedData, publicKey);
        result = decodedData == null ? null : new String(decodedData);
        System.err.println("公钥解密后: " + result);

        // 4.私钥产生签名
        System.out.println("私钥签名生成签名");
        // 注意：这里的encodedData，是私钥产生的（见74行）
        String sign = RSAUtil.sign(encodedData, privateKey);
        System.err.println("签名: " + sign);

        // 5.公钥验证签名
        System.out.println("公钥验证签名");
        Boolean status = RSAUtil.verify(encodedData, publicKey, sign);
        System.err.println("状态: " + status);
    }

    // jdk实现：
    private static void jdkRSA() {
        try {
            // 1.初始化发送方密钥
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(1024);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
            System.out.println("Public Key:" + Base64.encodeBase64String(rsaPublicKey.getEncoded()));
            System.out.println("Private Key:" + Base64.encodeBase64String(rsaPrivateKey.getEncoded()));

            // 2.私钥加密、公钥解密 ---- 加密
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            byte[] result = cipher.doFinal(encodeStr.getBytes());
            System.out.println("私钥加密、公钥解密 ---- 加密:" + Base64.encodeBase64String(result));

            // 3.私钥加密、公钥解密 ---- 解密
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
            keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            result = cipher.doFinal(result);
            System.out.println("私钥加密、公钥解密 ---- 解密:" + new String(result));


            // 4.公钥加密、私钥解密 ---- 加密
            X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
            KeyFactory keyFactory2 = KeyFactory.getInstance("RSA");
            PublicKey publicKey2 = keyFactory2.generatePublic(x509EncodedKeySpec2);
            Cipher cipher2 = Cipher.getInstance("RSA");
            cipher2.init(Cipher.ENCRYPT_MODE, publicKey2);
            byte[] result2 = cipher2.doFinal(encodeStr.getBytes());
            System.out.println("公钥加密、私钥解密 ---- 加密:" + Base64.encodeBase64String(result2));

            // 5.私钥解密、公钥加密 ---- 解密
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
            KeyFactory keyFactory5 = KeyFactory.getInstance("RSA");
            PrivateKey privateKey5 = keyFactory5.generatePrivate(pkcs8EncodedKeySpec5);
            Cipher cipher5 = Cipher.getInstance("RSA");
            cipher5.init(Cipher.DECRYPT_MODE, privateKey5);
            byte[] result5 = cipher5.doFinal(result2);
            System.out.println("公钥加密、私钥解密 ---- 解密:" + new String(result5));

        } catch (Exception e) {

            e.printStackTrace();
        }

    }
}

